package com.meelive.ingkee.iknetwork.diagnose.viewmodel

import android.annotation.SuppressLint
import android.content.Context
import android.os.Build
import android.os.Handler
import android.os.Looper
import androidx.lifecycle.ViewModel
import com.inke.inkenetinspector.InkeNetInspector
import com.meelive.ingkee.base.utils.android.PackageUtils
import com.meelive.ingkee.base.utils.collection.CollectionUtils
import com.meelive.ingkee.base.utils.concurrent.ThreadPools
import com.meelive.ingkee.base.utils.text.StringUtils
import com.meelive.ingkee.iknetwork.diagnose.DiagnoseProvider
import com.meelive.ingkee.iknetwork.diagnose.model.IKNetworkDiagnoseBaseInfo
import com.meelive.ingkee.iknetwork.diagnose.model.IKNetworkDiagnoseConfig
import com.meelive.ingkee.iknetwork.diagnose.util.InKeNetInspectorUtil
import com.meelive.ingkee.iknetwork.diagnose.util.InKeNetworkDiagnoseUtil
import com.netease.LDNetDiagnoService.LDNetTraceRoute
import com.netease.LDNetDiagnoService.LDNetTraceRoute.LDNetTraceRouteListener
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicInteger

@SuppressLint("CheckResult", "SimpleDateFormat")
class IKNetworkDiagnoseViewModel : ViewModel() {

    private var appContext: Context = DiagnoseProvider.getAppContext()
    private lateinit var networkDiagnoseConfig: IKNetworkDiagnoseConfig
    private var inKeNetInspector: InkeNetInspector? = null

    private val uiHandler: Handler
        get() = Handler(Looper.getMainLooper())

    private val diagnoseObservable: DiagnoseObservable by lazy {
        DiagnoseObservable()
    }
    private var traceRouteObservable: TraceRouteObservable? = null

    private val requestCount: AtomicInteger = AtomicInteger(0)
    private var scheduled: ScheduledExecutorService? = null
    private var pingCount: AtomicInteger? = null

    @Volatile
    private var isQuit = false

    @Volatile
    private var callback: ((MessageData) -> Unit)? = null

    fun analyzeConfig(baseInfo: IKNetworkDiagnoseBaseInfo?, config: IKNetworkDiagnoseConfig, callback: (MessageData) -> Unit) {
        networkDiagnoseConfig = config
        this.callback = callback
        val initSuccess = initInKeNetInspector()
        printBaseInfo(baseInfo)
        if (!initSuccess) {
            this.callback?.invoke(MessageData(message = "InKeNetInspector创建失败", error = true))
            return
        }
        printNetInfo()
    }

    /**
     * 启动InKeNetInspector，并配置接口
     */
    private fun initInKeNetInspector(): Boolean {
        InkeNetInspector.setLibraryEnable(true)
        try {
            inKeNetInspector = InkeNetInspector()
        } catch (e: Exception) {
            return false
        }
        val displayPipeline = InkeNetInspector.InkeNetUIDisplayPipeline { s, inkeInspectorType, b, inkeNetInspector, o ->
            diagnoseObservable.setLogInfo(s, inkeInspectorType, b)
        }
        inKeNetInspector?.setUIDisplayPipeline(displayPipeline, Any())
        return true
    }

    private fun printBaseInfo(baseInfo: IKNetworkDiagnoseBaseInfo?) {
        writeInLogOnUI("开始诊断...")
        val simpleDateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
        val res = simpleDateFormat.format(Date())
        writeInLogOnUI("诊断时间:$res")
        writeInLogOnUI("应用版本:${PackageUtils.getAppVersionName(appContext)}")
        writeInLogOnUI("用户ID:${baseInfo?.uid}")
        writeInLogOnUI("机器类型:${Build.MANUFACTURER}:${Build.BRAND}:${Build.MODEL}")
        writeInLogOnUI("系统版本:${Build.VERSION.RELEASE}")
        writeInLogOnUI("机器ID:${baseInfo?.iMei}")
        writeInLogOnUI("运营商:${baseInfo?.phoneCarrName}")
        writeInLogOnUI("ISOCountryCode:${baseInfo?.isoCountryCode}")
        writeInLogOnUI("MobileCountryCode:${baseInfo?.mobilCountryCode}")
        writeInLogOnUI("MobileNetworkCode:${baseInfo?.mobilNetCode}")
        writeInLogOnUI("")

        //是否联网
        val isNetConnected: Boolean
        if (InKeNetworkDiagnoseUtil.isNetworkConnected()) {
            isNetConnected = true
            writeInLogOnUI("当前是否联网:已联网")
        } else {
            isNetConnected = false
            writeInLogOnUI("当前是否联网:未联网")
        }

        //联网类型
        val netType: String? = InKeNetworkDiagnoseUtil.getNetWorkType(appContext)
        writeInLogOnUI("当前联网类型:$netType")

        //本地ip
        val localIp: String?
        //网关
        var gateWay: String? = null
        if (isNetConnected) {
            if (InKeNetworkDiagnoseUtil.NETWORKTYPE_WIFI == netType) { // wifi：获取本地ip和网关，其他类型：只获取ip
                localIp = InKeNetworkDiagnoseUtil.getLocalIpByWifi(appContext)
                gateWay = InKeNetworkDiagnoseUtil.pingGateWayInWifi(appContext)
            } else {
                localIp = InKeNetworkDiagnoseUtil.getLocalIpBy3G()
            }
            writeInLogOnUI("本地IP:$localIp")
        } else {
            writeInLogOnUI("本地IP:127.0.0.1")
        }
        if (gateWay != null) {
            writeInLogOnUI("本地网关:$gateWay")
        }
        if (isNetConnected) {
            writeInLogOnUI("本地DNS:${InKeNetworkDiagnoseUtil.getLocalDns("dns1")},${InKeNetworkDiagnoseUtil.getLocalDns("dns2")}")
        } else {
            writeInLogOnUI("本地DNS:0.0.0.0,0.0.0.0")
        }
        writeInLogOnUI("")
    }

    private fun printNetInfo() {
        DiagnoseObserver(diagnoseObservable)
        //创建一个定时任务
        scheduled = Executors.newScheduledThreadPool(2)
        //请求超时
        scheduled?.schedule(Runnable {
            if (requestCount.get() != 0) {
                diagnoseObservable.deleteObservers()
                //弹出未显示完的数据
                for ((key, value) in diagnoseObservable.pingMapLog.entries) {
                    writeInLog("Ping域名${key}(完成超时):\n$value")
                }
                diagnoseObservable.pingMapLog.clear()
                if (!StringUtils.isEmpty(diagnoseObservable.mNICSpeedMonitorLog.toString())) {
                    writeInLog("网卡检测(完成超时):${diagnoseObservable.mNICSpeedMonitorLog}")
                }
                if (!StringUtils.isEmpty(diagnoseObservable.cpuFreqLog.toString())) {
                    writeInLog("CPU检测(完成超时):${diagnoseObservable.cpuFreqLog}")
                }
                for ((key, value) in diagnoseObservable.downloadMapLog.entries) {
                    writeInLog("下载检测$key(完成超时):\n$value")
                }
                diagnoseObservable.downloadMapLog.clear()
                startTraceRoute()
            }
        }, 10, TimeUnit.SECONDS)

        //开启网卡检测
        inKeNetInspector?.apply {
            InKeNetInspectorUtil.httpStartNICSpeedMonitor(this)
        }
        if (networkDiagnoseConfig.ping_times > 0) {
            pingCount = AtomicInteger(networkDiagnoseConfig.ping_list.size)
            requestCount.addAndGet(networkDiagnoseConfig.ping_list.size)
            val hosts: ArrayList<String> = ArrayList<String>(networkDiagnoseConfig.ping_list)

            inKeNetInspector?.apply {
                InKeNetInspectorUtil.pingDomain(this, hosts, networkDiagnoseConfig.ping_times, networkDiagnoseConfig.ping_interval)
            }
        }
        if (networkDiagnoseConfig.cpu_freq == 1) {
            requestCount.getAndIncrement()

            inKeNetInspector?.apply {
                InKeNetInspectorUtil.calculateCpuFreq(this)
            }
        }
    }

    private fun writeInLogOnUI(message: String) {
        callback?.invoke(MessageData(message = "${message}\n"))
    }

    private fun writeInLog(message: String) {
        uiHandler.post {
            callback?.invoke(MessageData(message = message))
        }
    }

    private fun startTraceRoute() {
        traceRouteObservable = TraceRouteObservable().apply {
            TraceRouteObserver(this)
        }
        ThreadPools.IO_THREAD_POOL.get()
                .submit(Runnable {
                    val diagnose = LDNetTraceRoute.getInstance()
                    diagnose.initListenter(TraceRouterListener())
                    diagnose.isCTrace = true
                    writeInLog("Traceroute检测开始:")
                    val traceRouteList: List<String> = networkDiagnoseConfig.trace_route_list
                    for (domain in traceRouteList) {
                        if (isQuit) {
                            return@Runnable
                        }
                        writeInLog("\n检查路径:$domain\n")
                        diagnose.startTraceRoute(domain)
                    }
                    analyzeComplete()
                })
    }

    private fun analyzeComplete() {
        //试图关闭网卡检测
        inKeNetInspector?.apply {
            InKeNetInspectorUtil.httpStopNICSpeedMonitor(this)
            uiHandler.post {
                callback?.invoke(MessageData(message = quitWithResult(), completed = true))
            }
        }
        inKeNetInspector = null

        diagnoseObservable.apply {
            deleteObservers()
        }
        traceRouteObservable?.apply {
            deleteObservers()
        }
    }

    private inner class DiagnoseObserver constructor(diagnoseObservable: DiagnoseObservable) : Observer {

        init {
            diagnoseObservable.addObserver(this)
        }

        override fun update(o: Observable, arg: Any) {
            writeInLog(arg.toString())
            requestCount.getAndDecrement()
            if (requestCount.get() == 0) {
                startTraceRoute()
            }
        }
    }

    private inner class DiagnoseObservable : Observable() {
        val downloadMapLog: MutableMap<String, String> = HashMap()
        val pingMapLog: MutableMap<String, String> = HashMap()
        val cpuFreqLog = StringBuilder()
        val mNICSpeedMonitorLog = StringBuilder()
        fun setLogInfo(Info: String, inKeInspectorType: InkeNetInspector.InkeInspectorType?, complete: Boolean) {
            when (inKeInspectorType) {
                InkeNetInspector.InkeInspectorType.Ping -> createPingLog(Info, complete)
                InkeNetInspector.InkeInspectorType.CpuFreq -> createCpuFreqLog(Info, complete)
                InkeNetInspector.InkeInspectorType.HttpDownload -> createDownloadLog(Info, complete)
                InkeNetInspector.InkeInspectorType.NICSpeed, InkeNetInspector.InkeInspectorType.MaxValue, InkeNetInspector.InkeInspectorType.HttpUpload, InkeNetInspector.InkeInspectorType.TraceRoute -> {
                }
                else -> throw AssertionError()
            }
        }

        private fun createPingLog(Info: String, complete: Boolean) {
            if (complete) {
                val domain = Info.substring(Info.indexOf("ping ") + 4, Info.indexOf("("))
                val showMessage = pingMapLog[domain].toString() + Info
                val logInfo = "${showMessage}\nPing域名:${domain}完成\n\n"
                pingMapLog.remove(domain)
                pingCount?.getAndDecrement()
                if (pingCount?.get() == 0) {
                    val httpDownloadList: List<IKNetworkDiagnoseConfig.HttpList> = networkDiagnoseConfig.http_download_list
                    if (!CollectionUtils.isEmpty<IKNetworkDiagnoseConfig.HttpList>(httpDownloadList)) {
                        for (httpList in httpDownloadList) {
                            requestCount.getAndIncrement()
                            inKeNetInspector?.apply {
                                InKeNetInspectorUtil.httpDownload(this, httpList)
                            }
                        }
                    }
                }
                //加后再减，保证逻辑顺序
                setChanged()
                notifyObservers(logInfo)
            } else {
                val domain = Info.substring(Info.indexOf("from ") + 4, Info.indexOf("("))
                if (StringUtils.isEmpty(pingMapLog[domain])) {
                    pingMapLog[domain] = "$Info\n\n"
                } else {
                    val newMessage = "${pingMapLog[domain].toString()}$Info\n"
                    pingMapLog[domain] = newMessage
                }
            }
        }

        private fun createCpuFreqLog(Info: String, complete: Boolean) {
            if (complete) {
                cpuFreqLog.append(Info)
                val logInfo = "${cpuFreqLog}\ncpu检测结束\n\n"
                cpuFreqLog.delete(0, cpuFreqLog.length)
                setChanged()
                notifyObservers(logInfo)
            } else {
                cpuFreqLog.append(Info).append("\n")
            }
        }

        private fun createDownloadLog(Info: String, complete: Boolean) {
            val downPath = Info.substring(Info.indexOf("<") + 1, Info.indexOf(">"))
            if (complete) {
                val showMessage = downloadMapLog[downPath].toString() + Info
                val logInfo = "$showMessage\n下载:${downPath}完成\n\n"
                downloadMapLog.remove(downPath)
                setChanged()
                notifyObservers(logInfo)
            } else {
                if (StringUtils.isEmpty(downloadMapLog[downPath])) {
                    downloadMapLog[downPath] = "$Info\n"
                } else {
                    val newMessage = "${downloadMapLog[downPath].toString()}\n"
                    downloadMapLog[downPath] = newMessage
                }
            }
        }
    }

    private inner class TraceRouteObserver constructor(traceRouteObservable: TraceRouteObservable) : Observer {
        init {
            traceRouteObservable.addObserver(this)
        }

        override fun update(o: Observable, arg: Any) {
            writeInLog(arg.toString())
        }
    }

    private class TraceRouteObservable : Observable() {
        fun setLogInfo(info: String?) {
            setChanged()
            notifyObservers(info)
        }
    }

    private inner class TraceRouterListener : LDNetTraceRouteListener {
        override fun OnNetTraceUpdated(msg: String?) {
            var log = msg ?: return
            if (log.contains("ms") || log.contains("***")) {
                log += "\n"
            }
            traceRouteObservable?.setLogInfo(log)
        }

        override fun OnNetTraceFinished() {}
    }

    override fun onCleared() {
        super.onCleared()

        uiHandler.removeCallbacksAndMessages(null)

        isQuit = true

        callback = null

        //取消订阅
        diagnoseObservable.apply {
            deleteObservers()
        }
        traceRouteObservable?.apply {
            deleteObservers()
        }

        inKeNetInspector?.apply {
            //试图关闭网卡检测
            InKeNetInspectorUtil.httpStopNICSpeedMonitor(this)
            //关闭InKeNetInspector
            quitWithResult()
        }
        inKeNetInspector = null

        //关闭延迟任务
        scheduled?.apply {
            try {
                shutdownNow()
            } catch (e: java.lang.Exception) {
            }
        }
        scheduled = null
    }
}

data class MessageData(val message: String, val completed: Boolean = false, val error: Boolean = false)